package com.zac.coroutine.algorithm.leetcode.others

/**
 * author zac
 * date 2024/11/11
 * 202. 快乐数
 * 简单
 * 相关标签
 * 相关企业
 * 编写一个算法来判断一个数 n 是不是快乐数。
 *
 * 「快乐数」 定义为：
 *
 * 对于一个正整数，每一次将该数替换为它每个位置上的数字的平方和。
 * 然后重复这个过程直到这个数变为 1，也可能是 无限循环 但始终变不到 1。
 * 如果这个过程 结果为 1，那么这个数就是快乐数。
 * 如果 n 是 快乐数 就返回 true ；不是，则返回 false 。
 *
 *
 */
class Test15 {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {

            println("是否是快乐数 ${isHappy(19)}")
        }

        /**
         * 学习网上大神的解法:快慢指针
         * 首先要明确一个条件:
         * 网上大神的解释(
         * 快乐数的定义是基于一个计算过程，即对一个正整数，不断将其替换为它各个位上数字的平方和，如果最终这个过程能够收敛到1，则这个数被称为快乐数。
         * 相反，如果在这个过程中形成了一个不包含1的循环，则该数不是快乐数。对于非快乐数，它们的平方和序列会进入一个固定的循环，
         * 例如4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4。为了证明每个循环的数字都是一样的，我们可以使用数学中的不动点理论。
         * 在一个有限的系统中，重复应用一个确定的操作最终会达到一个循环，这是因为系统的状态是有限的。在快乐数的情况下，
         * 由于每次计算都是基于有限的数字（0-9）的平方，因此可能的结果也是有限的。这意味着，如果我们从某个数字开始，不断重复计算它的各位数字的平方和，最终必然会进入一个循环，
         * 因为可能的平方和是有限的，而且每次计算都是确定性的。此外，由于每个非快乐数都会进入一个固定的循环，而这个循环不包含1，这意味着循环中的所有数字都是固定的，
         * 并且每次遇到同一个数字时，都会得到相同的下一个数字。这就是为什么每个循环的数字都是一样的。综上所述，我们可以得出结论，
         * 对于非快乐数，它们在重复计算各位数字的平方和的过程中不仅会形成一个循环，而且每个循环中的数字都是一样的。
         * 这一结论是基于有限性原理和确定性操作的重复应用。)
         *
         * 于是使用一个快指针 和一个慢指针对这个数进行循环计算,那么迟早有一次两个数会相等,
         * 当其相等时,如果结果 == 1,则说明是快乐数 ,否则就不是快乐数.
         *
         */
        fun isHappy(n: Int): Boolean {
            var x = n
            var y = n
            do {
                x = add(x)
                y = add(y)
                y = add(y)
            } while (x != y)

            return x == 1
        }

        fun add(n: Int): Int {
            var t = n
            var s = 0
            // 这个写法值得学习,就是把整数的每一位拆出来 : 对10取余然后除以10
            while (t > 0) {
                val a = t % 10
                s += a * a
                t /= 10
            }

            return s
        }

    }
}